"""
nacos 官方文档
https://nacos.io/docs/latest/manual/user/python-sdk/usage/
https://pypi.org/project/nacos-sdk-python/#description
"""
from typing import List, Optional

from v2.nacos import ClientConfigBuilder, GRPCConfig, ClientConfig, NacosNamingService, RegisterInstanceParam, \
    DeregisterInstanceParam, ListInstanceParam, Instance

from app.core.config import settings
from app.core.log import logger
from app.utils.load_balancer import LoadBalancer

# 创建 nacos 客户端配置
_client_config: ClientConfig = (ClientConfigBuilder()
                                .server_address(settings.nacos_server_address)
                                .username(settings.nacos_username)
                                .password(settings.nacos_password)
                                .namespace_id(settings.nacos_namespace)
                                .log_level('INFO')
                                .log_dir(f'{settings.log_path}/nacos/')
                                .load_cache_at_start(False)
                                .grpc_config(GRPCConfig(grpc_timeout=5000))
                                .build())


class NacosConfiguration:
    """ nacos配置类 """

    def __init__(self):
        self.naming_client: Optional[NacosNamingService] = None
        self.registered: Optional[bool] = None

    async def register_service(self):
        """
        注册服务到Nacos注册中心

        通过NacosNamingService将当前服务实例注册到Nacos服务器，
        使其他服务能够发现和调用当前服务。
        """
        # 创建服务发现客户端
        if self.naming_client is None:
            self.naming_client = await NacosNamingService.create_naming_service(_client_config)
        try:
            # 注册服务
            self.registered = await self.naming_client.register_instance(
                request=RegisterInstanceParam(
                    service_name=settings.app_name,
                    group_name=settings.nacos_group,
                    ip=settings.instance_ip,
                    port=settings.app_port,
                    weight=settings.instance_weight,
                    metadata={
                        "preserved.register.source": "Fast_Api",
                    }
                )
            )
            logger.info("nacos 服务注册成功: {}", settings.app_name)
        except Exception as e:
            logger.exception("nacos 服务注册失败", e)

    async def deregister_service(self):
        """
        注销Nacos上的服务

        从Nacos注册中心注销当前服务实例，通常在服务关闭时调用。
        """
        try:
            if self.registered:
                # 注销服务
                await self.naming_client.deregister_instance(
                    request=DeregisterInstanceParam(
                        service_name=settings.app_name,
                        group_name=settings.nacos_group,
                        ip=settings.instance_ip,
                        port=settings.app_port,
                    )
                )
                # 关闭grpc客户端
                await self.naming_client.shutdown()
                logger.info("nacos 服务注销成功: {}", settings.app_name)
        except Exception as e:
            logger.exception("nacos 服务注销失败", e)

    async def get_server_instances(self, service_name: str, group_name: str = settings.nacos_group) -> List[Instance]:
        """
        获取服务实例列表

        从Nacos注册中心获取指定服务的所有健康实例列表。

        :param service_name: 服务名称
        :param group_name: 服务组名称，默认使用配置中的nacos_group
        :return: 服务实例列表
        """
        if not self.registered:
            logger.warning("服务尚未注册到Nacos，无法获取实例列表")
            return []
        try:
            # 感兴趣的可以去看看NacosServiceDiscovery.java 的 getInstances方法
            return await self.naming_client.list_instances(ListInstanceParam(
                service_name=service_name,
                group_name=group_name,
                healthy_only=True  # 是否仅查询健康实例
            ))
        except Exception as e:
            logger.exception(f"获取服务{service_name}实例列表失败", e)
            return []

    async def get_server_ip_address(self, service_name: str, group_name: str = settings.nacos_group) -> str:
        """
        获取服务实例IP地址

        通过服务发现获取指定服务的可用实例IP地址，并使用负载均衡算法选择一个实例。

        :param service_name: 服务名称
        :param group_name: 服务组名称，默认使用配置中的nacos_group
        :return: 选中的服务实例IP地址（包含协议和端口的完整URL）
        :raises Exception: 当没有找到服务实例时抛出异常
        """
        instances = await self.get_server_instances(service_name, group_name)
        if not instances:
            raise Exception(f"没有找到服务实例 {service_name}")

        # 提取实例的IP地址
        def extract_ip_addr(instance: Instance) -> str:
            return f"http://{instance.to_inet_addr()}"
        ip_addr_list = list(map(extract_ip_addr, instances))

        # 使用轮询算法, 按顺序依次选择
        load_balancer = LoadBalancer(servers=ip_addr_list)
        return load_balancer.round_robin()


# 实例化Nacos配置对象
nacos_config = NacosConfiguration()


async def register_service():
    """ 注册服务到Nacos """
    await nacos_config.register_service()


async def deregister_service():
    """ 注销Nacos上的服务 """
    await nacos_config.deregister_service()
